#include "Log.h"
#include "Util.h"
#include "buffer.h"
#include <HttpContext.h>
#include <cstddef>
#include <string>
#include <vector>

namespace muduo{
    // ============================HttpContext-private============================
    bool HttpContext::ParseHttpLine(std::string& line){
        // 使用正则表达式进行匹配
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        bool ret = std::regex_match(line, matches, e);
        if (ret == false) {
            _recv_statu = RECV_HTTP_ERR;
            _resp_statu = 400; // BAD REQUEST
            return false;
        }
        //0 : GET /bitejiuyeke/login?user=xiaoming&pass=123123 HTTP/1.1
        //1 : GET
        //2 : /bitejiuyeke/login
        //3 : user=xiaoming&pass=123123
        //4 : HTTP/1.1
        // 获取请求方法
        _request._method = matches[1];
        std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);
        // 获取请求路径,获取时不需要进行+ -> ' '
        _request._path = Util::UrlDecode(matches[2],false);
        // 获取请求字符串
        std::vector<std::string> query_string_array;
        std::string query_string;
        Util::Split(matches[3], "&", &query_string_array);
        for (auto &str :query_string_array){
            int pos = str.find('=');
            if(pos == std::string::npos){
                _recv_statu = RECV_HTTP_ERR;
                _resp_statu = 400; // BAD REQUEST
                return false;
            }
            std::string key = Util::UrlDecode(str.substr(0,pos), true);
            std::string val = Util::UrlDecode(str.substr(pos+1), true);
            _request.SetParam(key, val);
        }
        // 获取协议版本
        _request._version = matches[4];
        return true;
    }
    bool HttpContext::RecvHttpLine(Buffer* buf){
        // 获得一行内容
        std::string line= buf->GetLineAndPop();
        // 获得的内容不足一行
        if(line.size()==0){ 
            if(buf->EnableReadSize() > MAX_LINE){
                _recv_statu = RECV_HTTP_ERR;
                _resp_statu = 414;//URI TOO LONG
                return false;
            }
            // 到此处说明获得的内容数据不完整,等新的数据到来
            return true;
        }
        // 如果接收到的数据太长
        if(line.size() > MAX_LINE){
            _recv_statu = RECV_HTTP_ERR;
            _resp_statu = 414;//URI TOO LONG
            return false;
        }
        // 接收到了完整的数据，可以开始解析
        bool ret = ParseHttpLine(line);
        if(ret == false){
            return false;
        }
        // 设置接收状态码为RECV_HTTP_HEAD,进入头部获取阶段
        _recv_statu =RECV_HTTP_HEAD;
        return true;
    }
    bool HttpContext::ParseHttpHead(std::string& line){
        // 头部的格式为key: val\r\nkey: val\r\n....
        // 注意去掉末尾的字符，负责插入的val中就包含"\r\n"
        if(line.back() == '\n') line.pop_back();
        if(line.back() == '\r') line.pop_back();
        int pos = line.find(": ");
        if(pos == std::string::npos){
            _recv_statu = RECV_HTTP_ERR;
            _resp_statu = 400; // BAD REQUEST
            return false;
        }
        std::string key = line.substr(0,pos);
        std::string val = line.substr(pos+2);
        _request.SetHeader(key,val);
        return true;
    }
    bool HttpContext::RecvHttpHead(Buffer* buf){
        if(_recv_statu != RECV_HTTP_HEAD) return false;
        // 头部的格式为key: val\r\nkey: val\r\n....，所以需要不断取出数据，直到接收到空行（也就是head与body中间的空行）为止
        while(1){
            std::string line= buf->GetLineAndPop();
            // 获得的内容不足一行
            if(line.size()==0){ 
                if(buf->EnableReadSize() > MAX_LINE){
                    _recv_statu = RECV_HTTP_ERR;
                    _resp_statu = 414;//URI TOO LONG
                    return false;
                }
                // 到此处说明获得的内容数据不完整,等新的数据到来
                return true;
            }
            // 如果接收到的数据太长
            if(line.size() > MAX_LINE){
                debuglog;
                _recv_statu = RECV_HTTP_ERR;
                _resp_statu = 414;//URI TOO LONG
                return false;
            }
            // 说明接收到了空行，break
            if(line == "\n" || line == "\r\n"){
                break;
            }
            // 可以解析Head中的内容
            bool ret = ParseHttpHead(line);
            if(ret ==false){
                debuglog;
                return false;
            }

        }
        // 处理完毕，进入正文获取阶段
        _recv_statu =RECV_HTTP_BODY ;
        return true;
    }
    bool HttpContext::RecvHttpBody(Buffer* buf){
        if(_recv_statu != RECV_HTTP_BODY) return false;
        // 1.获取正文长度
        size_t body_len=_request.ContentLength();
        if(body_len == 0){
            // 没有正文，直接返回
            _recv_statu =RECV_HTTP_OVER;
            return true;
        }
        // 2.判断_request._body还需要接收的正文长度是多少
        size_t need_len = body_len - _request._body.size();
        // 3.如果buffer中的可读长度>=正文的长度，则放入
        if(buf->EnableReadSize() >= need_len){
            _request._body.append(buf->GetReadIndex(),need_len);
            buf->MoveReadidx(need_len);
            _recv_statu =RECV_HTTP_OVER;
            return true;
        }
        // 4.当正文长度不足时，也要放入到body中，等待下次数据到来
        _request._body.append(buf->GetReadIndex(),buf->EnableReadSize());
        buf->MoveReadidx(buf->EnableReadSize());
        return true;
    }
    //============================HttpContext-public============================
    HttpContext::HttpContext():_resp_statu(200),_recv_statu(RECV_HTTP_LINE){}
    void HttpContext::Reset(){
        _request.Reset();
        _resp_statu=200;
        _recv_statu =  RECV_HTTP_LINE;
    }
    HttpRequest& HttpContext::Request(){return _request;}
    int HttpContext::RespStatu(){return _resp_statu;}
    HttpRecvStatu HttpContext::Recvstatu(){return _recv_statu;}
    void HttpContext::RecvHttpRequest(Buffer* buf){
        // 根据状态去处理请求
        switch(_recv_statu){
            case RECV_HTTP_LINE:
                RecvHttpLine(buf);
            case RECV_HTTP_HEAD:
                RecvHttpHead(buf);
            case RECV_HTTP_BODY:
                RecvHttpBody(buf);
        }
    }
}